package cn.seaboot.admin.logger.manager;

import cn.seaboot.admin.logger.bean.ServiceLog;
import cn.seaboot.admin.logger.service.AccessLogService;
import cn.seaboot.admin.logger.service.ServiceLogService;
import cn.seaboot.admin.user.bean.User;
import cn.seaboot.commons.core.CommonUtils;
import cn.seaboot.commons.core.FastJsonUtils;
import cn.seaboot.commons.core.Tokenizer;
import cn.seaboot.commons.lang.Null;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * 日志存储
 *
 * @author Mr.css
 * @version 2024-08-23 9:20
 */
public class LoggerRunnable implements Runnable {

    private final ProceedingJoinPoint joinPoint;

    private final ServiceLogService serviceLogService;

    private final User user;

    private final Object result;

    public LoggerRunnable(ProceedingJoinPoint joinPoint, ServiceLogService serviceLogService, User user, Object result) {
        this.joinPoint = joinPoint;
        this.serviceLogService = serviceLogService;
        this.user = user;
        this.result = result;
    }

    @Override
    public void run() {
        ServiceLog log = new ServiceLog();

        // class - method
        Class<?> clazz = joinPoint.getTarget().getClass();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        String className = clazz.getName();
        log.setClassName(className);
        log.setClassName(method.getName());

        // 如果是 Controller，可以记录请求路径
        if (className.endsWith("Ctrl") || className.endsWith("Controller")) {
            RequestMapping requestMapping = this.getMethodRequestMapping(method);
            if (requestMapping != null) {
                RequestMethod[] methods = requestMapping.method();
                if (CommonUtils.isEmpty(methods)) {
                    log.setRequestMethod("ALL");
                } else {
                    log.setRequestMapping(methods[0].name());
                }

                String[] paths = requestMapping.value();
                if (CommonUtils.isEmpty(paths)) {
                    log.setRequestMapping("");
                } else {
                    log.setRequestMapping(paths[0]);
                }
            }
        }

        ActionLogger logger = method.getAnnotation(ActionLogger.class);

        // parameter
        String[] names = signature.getParameterNames();
        Object[] args = joinPoint.getArgs();

        // 获取所有的参数
        Map<String, Object> params = new HashMap<>();
        if (names != null) {
            for (int i = 0; i < names.length; i++) {
                params.put(names[i], args[i]);
            }
        }
        // 移除不必记录的参数
        String[] exclusive = logger.exclusive();
        if (CommonUtils.isNotEmpty(exclusive)) {
            for (String s : exclusive) {
                params.remove(s);
            }
        }
        log.setParameter(FastJsonUtils.toJSONString(params));

        // logger type, level
        log.setLogType(logger.type());
        log.setLogLevel(logger.level().value());
        log.setOperation(logger.operation());


        // user info
        log.setOrgId(user.getOrgId());
        log.setGroupId(user.getDeptId());
        log.setUserId(user.getId());
        log.setUserName(user.getUsername());


        if (result instanceof Exception) {
            String msg = CommonUtils.getStackTraceString((Throwable) result);
            log.setException(msg);
        } else {
            String ret = FastJsonUtils.toJSONString(result);
            log.setReturnValue(ret);
        }

        serviceLogService.insert(log);
    }

    /**
     * 获取 @RequestMapping 注解，兼容 restful 场景。
     * <p>
     * Get @RequestMapping instance form method.
     *
     * @param method method
     * @return RequestMapping
     */
    private RequestMapping getMethodRequestMapping(Method method) {
        Annotation[] annotations = method.getAnnotations();
        if (CommonUtils.isNotEmpty(annotations)) {
            for (Annotation annotation : annotations) {
                if (annotation.annotationType().equals(RequestMapping.class)) {
                    return (RequestMapping) annotation;
                } else {
                    RequestMapping requestMapping = annotation.getClass().getAnnotation(RequestMapping.class);
                    if (requestMapping != null) {
                        return requestMapping;
                    }
                }
            }
        }
        return null;
    }
}
